<?php
/*
	Project:
		RedirectTester

	File Version:
		0.1b

	Author(s):
		John Jimenez

	License:
		This script is release under the GPL licence.
		http://opensource.org/licenses/gpl-license.php

	Description:
		[Description]

	Notes:
		[Notes]

	Testing:
		[testing]
*/

$MAX_REDIRECTS = 20; // Maximum redirects accepted
$REPORT_ALL = false; // Set to True to see things that passed as well.

if (!$argv[1]) {
	echo "You must specify which site you want to test on.\n";
	exit(2);
}
else {
	$host = $argv[1];
	// Clean up host
		// Strip Trailing Slash
	if ( substr($host,-1) == '/' ) { $host = substr($host,0,-1); }
}

//Get Input
while (!feof(STDIN)) {
  $contents .= fread(STDIN, 8192);
}

if ($contents) {
	
	$op ='';
	$error_report = '';
	
	// Clean up on the data before splitting it into an array
	$contents = str_replace('%26','&',$contents); // Replace %26 with &
	$contents = str_replace('%27','\'',$contents); // Replace %27 with '
	$contents = str_replace('%2C',',',$contents); // Replace %2c with ,

	$lines = explode("\n",$contents);
	
	// Reverse Alpha the Lines
	natcasesort($lines);
	$lines = array_reverse($lines);
	
	$line_counter = 0;
	$error_counter = 0;

	foreach ($lines as $line) {
		$line_counter++;
		
		$report_issue = false;
		
		$fields = explode("\t",$line);
		
		if (!empty($fields[0])) {
			
			// Source Cleanup
				// Force leading string on destination
			if ( substr($fields[0],0,1) != '/' ) {
				$fields[0] = '/'.$fields[0];
			}
			
				// Force trailing slash on source if not a file
			if (strpos($fields[0],'.') === false && substr($fields[0],-1) != '/') {
				$fields[0] = $fields[0].'/';
			}
			
			// Destination Clean up (only if NOT "kill")
			if (strtolower($fields[1]) != 'kill') {
				// Force leading string on destination
				if ( substr($fields[1],0,1) != '/' ) {
					$fields[1] = '/'.$fields[1];
				}
				
				// Force trailing slash on destination if not a file
				if (strpos($fields[1],'.') === false && substr($fields[1],-1) != '/') {
					$fields[1] = $fields[1].'/';
				}
			}

			// Skip case swapping
			if (strtolower($fields[0]) == strtolower($fields[1])) {
				$newUrl = '';
			}
			else {
				$newUrl = $host.$fields[0]; // New URL to test
			}

			$redirCount = 0;
			$redirPaths = array(); // Collection of paths already tried to short circuit recursion.

			if ($newUrl) {
				do {
	
					if ($redirCount > 0) {
						$op .= "\n\tRedirect ".$redirCount.": ";
					}
					
					$op .= "Testing URL: \"".str_replace($host,"",$newUrl)."\"";
					
					if ($redirCount == 0) {
						$expected = (strtolower($fields[1]) != 'kill')? "\"".$fields[1]."\"":'404';
						$op .= "\n\t> Expecting Redirect to ".$expected."";
					}
					
					$headers = makeCurlCall($newUrl);
	
					$http_response = '';
	
					// First Extract the http Response:
					preg_match('/HTTP.* (\d{3}).*/iU', $headers, $http_response);
	
					$http_response = trim($http_response[1]);
					
					switch ($http_response) {
						case '404':
							$op .= "\n\t> 404";
							if (strtolower($fields[1]) == 'kill') {
								$op .= "\nProperly 404ing!";
							}
							else {
								$op .= "\nUnexpected 404!";
								$report_issue = true;
							}
							$newUrl = '';
							break;
						case '200':
							$op .= "\n\t> 200 Successfully Loaded.";
							
							// Test if we are on the expected page
							$shortLocation = str_replace($host,"",$newUrl);
							if (urldecode($fields[1]) == urldecode($shortLocation)) {
								$op .= "\nLanded on expected page!";
							}
							else {
								$report_issue = true;
								$op .= "\nFail! Expected:\"".$fields[1]."\" Actual: \"".$shortLocation."\"";
								$op.="\nHeader Dump:\n";
								$op.=$headers;
							}
							
							$newUrl = '';
							break;
						case '301':
							$redirCount++;
							// Extract the location
							preg_match('/Location: (.*)/', $headers, $location);
							$location = trim($location[1]);
							$shortLocation = str_replace($host,"",$location);
	
							$op .= "\n\t> Redirecting to \"".$shortLocation."\"";
	
							if (in_array($shortLocation,$redirPaths)) {
								$report_issue = true;
								$op .= "\n! Recursion Detected. No longer trying this path.";
								$newUrl = '';
							}
							else {
								$redirPaths[] = $shortLocation;
								$newUrl = $location;
							}
							break;
						default:
							$report_issue = true;
							if ($http_response == '302') {
								$op .= "\nFAIL! Appears to have been caught before the redirect.";
							}
							else {
								$op .= "\nFAIL! Unexpected Response Code: ".$http_response;
							}
							$op.="\nHeader Dump:\n";
							$op.=$headers;
							$newUrl = '';
							break;
					}
	
				} while ($redirCount <= $MAX_REDIRECTS && !empty($newUrl));
				
				if ($redirCount >= $MAX_REDIRECTS) {
					$report_issue = true;
					$op .= "\n\tExceeded Redirect Max!\nAppears to be a loop!";
				}
			}
			$op .= "\n\n";
		}
		
		if ($report_issue) {
			$error_counter++;
		}
		
		if ($report_issue || $REPORT_ALL) {
			echo $op;
		}
		$op = '';
	}
}
echo "\n";
echo $line_counter." lines tested.\n";
echo $error_counter." issues found.";

function makeCurlCall($url) {
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_HEADER, TRUE);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, FALSE);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	curl_setopt($ch, CURLOPT_NOBODY, TRUE);
	$a = curl_exec($ch);
	
	return $a;
}

?>